﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Utility.Data.DbClient;
using Utility.DBUtility.DatabaseOperate;
using Utility.DBUtility.ORM.EntityAttribute;

namespace Utility.DBUtility.ORM
{
    /// <summary>
    /// ORM 仓储
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public abstract class Repository<T>: IRepository<T> where T: Contracts.EntityBase
    {
        #region
        /// <summary>
        /// 事务ID,作为事务开启和关闭的ID号
        /// </summary>
        public string Transactionid
        {
            get {
                return DbHelper.Transactionid;
            }
            set
            {
                DbHelper.Transactionid = value;
            }
        }
        /// <summary>
        /// 开启事务, 使用事务必须设定每个Data实例的 Transactionid. 相同的Transactionid 才会在一个事务中提交
        /// 一个Data实例开启事务,相同 Transactionid 的Data实例都将使用到事务
        /// [目前不支持存储过程放入事务中]
        /// </summary>
        /// <param name="connectionString"></param>
        /// <param name="isolationLevel">事务开启级别</param>
        /// <returns>
        /// 返回事务ID,作为事务开启和关闭的ID号,同时也作为其它Data使用的数据库连接ID与事务ID
        /// </returns>
        public string BeginTransaction(string connectionString = "", IsolationLevel isolationLevel = IsolationLevel.ReadCommitted)
        {
            return DbHelper.BeginTransaction(connectionString, isolationLevel);
        }
        /// <summary>
        /// 提交事务
        /// 提交事务后,将清空链接对象
        /// </summary>
        public void Commit()
        {
            DbHelper.Commit();
        }
        /// <summary>
        /// 回滚事务
        /// 回滚事务后,将清空链接对象
        /// </summary>
        public void Rollback()
        {
            DbHelper.Rollback();
        }
        #endregion

        #region IEntityBaseOperate 方法
        /// <summary>
        /// 数据库操作类
        /// </summary>
        protected abstract DbHelperBase DbHelper { get; set; }
        /// <summary>
        /// 根据泛型属性 获取表结构信息
        /// </summary>
        private ORM.EntityAttribute.DbTableAttribute m_DbTableAttribute = typeof(T).GetCustomAttribute(typeof(DbTableAttribute), true) as DbTableAttribute;
        /// <summary>
        /// 实例对象 数据库表结构信息
        /// </summary>
        public virtual ORM.EntityAttribute.DbTableAttribute DbTableAttribute { get { return m_DbTableAttribute; } }
        /// <summary>
        /// 当前EntityBase属性
        /// </summary>
        private static List<PropertyInfo> m_PropertyInfolist = typeof(T).GetProperties().ToList();
        /// <summary>
        /// 实例对象 数据库表字段信息
        /// </summary>
        List<ORM.EntityAttribute.DbColumnAttribute> m_DbColumnAttributes = GetColumnInfo();
        List<ORM.EntityAttribute.DbColumnAttribute> m_PrimaryList = GetPrimaryColumnInfo();
        /// <summary>
        /// 插入或者更新,
        /// 根据主键进行查找数据. 没有则插入,有则更新,
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public virtual int AddOrUpdate(T model)
        {
            int count = 0;
            try
            {
                DbHelper.AutoBeginTransaction();
                if (this.Exists(model))
                {
                    count = this.Update(model);
                }
                else
                {
                    count = this.Add(model);
                }
                DbHelper.AutoCommit();
                return count;
            }
            catch (Exception ex)
            {
                DbHelper.AutoRollback();
                count = 0;
            }
            return count;
        }
        /// <summary>
        /// 插入或者更新,
        /// 根据主键进行查找数据. 没有则插入,有则更新,
        /// </summary>
        /// <param name="models"></param>
        /// <param name="action">
        /// 处理进度
        /// string: 当前处理的类型
        /// int1: 总数据量
        /// int2: 当前处理量
        /// </param>
        /// <returns></returns>
        public virtual int AddOrUpdate(List<T> models,Action<string,int,int> action=null)
        {
            int count = 0;
            if (null == models || models.Count == 0)
            {
                return count;
            }
            try
            {
                DbHelper.AutoBeginTransaction();
                if (models.Count > 500)
                {
                    ParallelOptions op = new ParallelOptions();
                    op.MaxDegreeOfParallelism = models.Count / 100;
                    if (op.MaxDegreeOfParallelism > 10)
                    {
                        op.MaxDegreeOfParallelism = 10; //最多线程数
                    }
                    Parallel.ForEach(models, op, (item, loopState) =>
                    {
                        if (this.Exists(item))
                        {
                            System.Threading.Interlocked.Add(ref count, this.Update(item));
                        }
                        else
                        {
                            System.Threading.Interlocked.Add(ref count, this.Add(item));
                        }
                        if (null != action)
                        {
                            action.BeginInvoke(DbTableAttribute.TableName, models.Count, count, null, null);
                        }
                    });
                }
                else
                {
                    for (int i = 0; i < models.Count; i++)
                    {
                        if (this.Exists(models[i]))
                        {
                            count += this.Update(models[i]);
                        }
                        else
                        {
                            count += this.Add(models[i]);
                        }
                        if (null != action)
                        {
                            action.BeginInvoke(DbTableAttribute.TableName, models.Count, i + 1, null, null);
                        }
                    }
                }
                DbHelper.AutoCommit();
                return count;
            }
            catch (Exception ex)
            {
                DbHelper.AutoRollback();
                count = 0;
            }
            return count;
        }
        /// <summary>
        /// 添加Model list
        /// </summary>
        /// <param name="models"></param>
        /// <param name="action">
        /// 处理进度
        /// string: 当前处理的类型
        /// int1: 总数据量
        /// int2: 当前处理量
        /// </param>
        /// <returns></returns>
        public virtual int Add(List<T> models, Action<string,int, int> action = null)
        {
            int count = 0;
            if (null == models || models.Count == 0)
            {
                return count;
            }
            try
            {
                DbHelper.AutoBeginTransaction();
                for (int i = 0; i < models.Count; i++)
                {
                    count += this.Add(models[i]);
                    if (null != action)
                    {
                        action.BeginInvoke(DbTableAttribute.TableName, models.Count, i + 1, null, null);
                    }
                }
                DbHelper.AutoCommit();
                return count;
            }
            catch (Exception ex)
            {
                DbHelper.AutoRollback();
                count = 0;
            }
            return count;
        }
        /// <summary>
        /// 更新 Model list
        /// </summary>
        /// <param name="models"></param>
        /// <param name="action">
        /// 处理进度
        /// string: 当前处理的类型
        /// int1: 总数据量
        /// int2: 当前处理量
        /// </param>
        /// <returns></returns>
        public virtual int Update(List<T> models, Action<string ,int, int> action = null)
        {
            int count = 0;
            if (null == models || models.Count == 0)
            {
                return count;
            }
            try
            {
                DbHelper.AutoBeginTransaction();
                for (int i = 0; i < models.Count; i++)
                {
                    count += this.Update(models[i]);
                    if (null != action)
                    {
                        action.BeginInvoke(DbTableAttribute.TableName, models.Count, i + 1, null, null);
                    }
                }
                DbHelper.AutoCommit();
                return count;
            }
            catch (Exception ex)
            {
                DbHelper.AutoRollback();
                count = 0;
            }
            return count;
        }
        /// <summary>
        /// 增加一条数据
        /// </summary>
        public virtual int Add(T model)
        {
            if (CheckColumnInfoFail(model) || model.PropertyChanges.Count == 0)
            {
                return 0;
            }
            //model.PropertyChanges
            StringBuilder strSql = new StringBuilder();
            List<DBParameter> dbParameters = new List<DBParameter>();

            string columns = string.Empty;
            string columnValues = string.Empty;
            Type typeObj = typeof(T);
            List<ORM.EntityAttribute.DbColumnAttribute> dbColumns = m_DbColumnAttributes.FindAll(t => model.PropertyChanges.Contains(t.PropertyName));
            dbColumns.ForEach(t =>
            {
                if (columns.Length > 0)
                {
                    columns += ",";
                    columnValues += ",";
                }
                columns += t.ColumnName;
                columnValues += DbHelper.DataParamterPrefixChar + t.ColumnName;

                DBParameter parameter = new DBParameter(DbHelper.DataParamterPrefixChar + t.ColumnName);
                parameter.DbType = t.DbType;
                parameter.Size = t.Length;
                parameter.Value = typeObj.GetProperty(t.PropertyName).GetValue(model);
                dbParameters.Add(parameter);
            });

            strSql.Append("INSERT INTO ");
            strSql.Append(DbTableAttribute.TableName);
            strSql.Append(" (");
            strSql.Append(columns);
            strSql.Append(")");
            strSql.Append(" VALUES ");
            strSql.Append("(");
            strSql.Append(columnValues);
            strSql.Append(")");
            int count = DbHelper.ExecuteSql(strSql.ToString(), dbParameters.ToArray());
            if (count > 0)
            {
                model.ClearChanges();
            }
            return count;
        }
        /// <summary>
        /// 更新一条数据[需要Model的 主键设定值]
        /// </summary>
        public virtual int Update(T model)
        {
            if (CheckColumnInfoFail(model) || model.PropertyChanges.Count == 0)
            {
                return 0;
            }

            StringBuilder strSql = new StringBuilder();
            List<DBParameter> dbParameters = new List<DBParameter>();

            strSql.Append("UPDATE ");
            strSql.Append(DbTableAttribute.TableName);
            strSql.Append(" SET ");
            Type typeObj = typeof(T);
            string columns = string.Empty;
            List<ORM.EntityAttribute.DbColumnAttribute> dbColumns = m_DbColumnAttributes.FindAll(t => model.PropertyChanges.Contains(t.PropertyName));

            dbColumns.ForEach(t =>
            {
                if (columns.Length > 0)
                {
                    columns += ",";
                }
                columns += t.ColumnName;
                columns += " =";
                columns += DbHelper.DataParamterPrefixChar + t.ColumnName;

                DBParameter parameter = new DBParameter(DbHelper.DataParamterPrefixChar + t.ColumnName);
                parameter.DbType = t.DbType;
                parameter.Size = t.Length;
                parameter.Value = typeObj.GetProperty(t.PropertyName).GetValue(model);
                dbParameters.Add(parameter);
            });
            strSql.Append(columns);

            strSql.Append(" WHERE ");

            strSql.Append(BuilderSqlWhereForUpdate(model, m_PrimaryList, dbParameters, false));
            int count = DbHelper.ExecuteSql(strSql.ToString(), dbParameters.ToArray());
            if (count > 0)
            {
                model.ClearChanges();
            }
            return count;
        }
        /// <summary>
        /// 删除一条数据[需要Model的 主键设定值]
        /// </summary>
        /// <param name="action">
        /// 处理进度
        /// string: 当前处理的类型
        /// int1: 总数据量
        /// int2: 当前处理量
        /// </param>
        public virtual int Delete(List<T> models, Action<string, int, int> action = null)
        {
            int count = 0;
            if (null == models || models.Count == 0)
            {
                return count;
            }
            try
            {
                DbHelper.AutoBeginTransaction();
                for (int i = 0; i < models.Count; i++)
                {
                    count += this.Delete(models[i]);
                    if (null != action)
                    {
                        action.BeginInvoke(DbTableAttribute.TableName, models.Count, i + 1, null, null);
                    }
                }
                DbHelper.AutoCommit();
                return count;
            }
            catch (Exception ex)
            {
                DbHelper.AutoRollback();
                count = 0;
            }
            return count;
        }
        /// <summary>
        /// 删除一条数据[需要Model的 主键设定值]
        /// </summary>
        public virtual int Delete(T model)
        {
            if (CheckColumnInfoFail(model))
            {
                return 0;
            }
            StringBuilder strSql = new StringBuilder();
            List<DBParameter> dbParameters = new List<DBParameter>();
            strSql.Append("DELETE FROM ");
            strSql.Append(DbTableAttribute.TableName);
            strSql.Append(" WHERE ");

            strSql.Append(BuilderSqlWhere(model, m_PrimaryList, dbParameters, false));
            int count = DbHelper.ExecuteSql(strSql.ToString(), dbParameters.ToArray());
            if (count > 0)
            {
                model.ClearChanges();
            }
            return count;
        }
        /// <summary>
        /// 查找数据[根据设置的Model值作为查询条件,进行选择]
        /// </summary>
        /// <param name="model"></param>
        /// <param name="isClearChanges">是否清除项次改变值</param>
        /// <returns></returns>
        public virtual T GetModel(T model, bool isClearChanges = true)
        {
            if (CheckColumnInfoFail(model))
            {
                return default(T);
            }

            StringBuilder strSql = new StringBuilder();
            List<DBParameter> dbParameters = new List<DBParameter>();

            strSql.Append("SELECT * FROM ");
            strSql.Append(DbTableAttribute.TableName);
            strSql.Append(" WHERE ");
            List<ORM.EntityAttribute.DbColumnAttribute> dbColumns = m_DbColumnAttributes.FindAll(t => model.PropertyChanges.Contains(t.PropertyName));
            strSql.Append(BuilderSqlWhere(model, dbColumns, dbParameters, false));
            var r = DbHelper.Query<T>(strSql.ToString(), dbParameters.ToArray()).FirstOrDefault();
            if (null != r && isClearChanges)
            {
                r.ClearChanges();
            }
            return r;
        }
        /// <summary>
        /// 获取数据列
        /// </summary>
        public virtual List<T> GetList()
        {
            StringBuilder strSql = new StringBuilder();
            strSql.Append("SELECT * FROM ");
            strSql.Append(DbTableAttribute.TableName);
            return DbHelper.Query<T>(strSql.ToString());
        }
        /// <summary>
        /// 获取数据列
        /// </summary>
        public virtual List<T> GetList(T model)
        {
            if (CheckColumnInfoFail(model))
            {
                return GetList();
            }
            StringBuilder strSql = new StringBuilder();
            List<DBParameter> dbParameters = new List<DBParameter>();
            strSql.Append("SELECT * FROM ");
            strSql.Append(DbTableAttribute.TableName);
            strSql.Append(" WHERE ");
            List<ORM.EntityAttribute.DbColumnAttribute> dbColumns = m_DbColumnAttributes.FindAll(t => model.PropertyChanges.Contains(t.PropertyName));
            strSql.Append(BuilderSqlWhere(model, dbColumns, dbParameters, false));
            return DbHelper.Query<T>(strSql.ToString(), dbParameters.ToArray());
        }
        /// <summary>
        /// 获取数据列,根据设定的Model值进行查询
        /// </summary>
        public virtual List<T> GetList(T model,string orderBy, out int totalCount, int pageIndex = 1, int pageSize = 20)
        {
            StringBuilder strSql = new StringBuilder();
            List<DBParameter> dbParameters = new List<DBParameter>();
            strSql.Append("SELECT * FROM ");
            strSql.Append(DbTableAttribute.TableName);            
            List<ORM.EntityAttribute.DbColumnAttribute> dbColumns = m_DbColumnAttributes.FindAll(t => model.PropertyChanges.Contains(t.PropertyName));
            if (null != dbColumns && dbColumns.Count > 0)
            {
                strSql.Append(" WHERE ");
                strSql.Append(BuilderSqlWhere(model, dbColumns, dbParameters, false));
            }
            if (!string.IsNullOrEmpty(orderBy))
            {
                strSql.Append(" ORDER BY ");
                strSql.Append(orderBy);
            }
            return DbHelper.Query<T>(strSql.ToString(), out totalCount, pageIndex, pageSize, dbParameters.ToArray());
        }
        /// <summary>
        /// 获取数据列,根据设定的Model值进行查询
        /// </summary>
        public virtual List<T> GetList(string sqlWhere, string orderBy, out int totalCount, int pageIndex = 1, int pageSize = 20, params DBParameter[] cmdParms)
        {
            StringBuilder strSql = new StringBuilder();
            strSql.Append("SELECT * FROM ");
            strSql.Append(DbTableAttribute.TableName);
            if (!string.IsNullOrEmpty(sqlWhere))
            {
                strSql.Append(" WHERE ");
                strSql.Append(sqlWhere);
            }
            if (!string.IsNullOrEmpty(orderBy))
            {
                strSql.Append(" ORDER BY ");
                strSql.Append(orderBy);
            }
            return DbHelper.Query<T>(strSql.ToString(), out totalCount, pageIndex, pageSize, cmdParms);
        }
        /// <summary>
        /// 是否存在指定条件的数据[需要Model的 主键设定值]
        /// </summary>
        public virtual bool Exists(T model)
        {
            if (CheckColumnInfoFail(model))
            {
                return false;
            }

            StringBuilder strSql = new StringBuilder();
            List<DBParameter> dbParameters = new List<DBParameter>();

            strSql.Append("SELECT COUNT(1) FROM ");
            strSql.Append(DbTableAttribute.TableName);
            strSql.Append(" WHERE ");

            strSql.Append(BuilderSqlWhere(model, m_PrimaryList, dbParameters, false));

            return DbHelper.ExecuteScalar<int>(strSql.ToString(), dbParameters.ToArray()) > 0;
        }
        /// <summary>
        /// 软删除数据[需要Model的 主键设定值]
        /// 如果表不支持软删除,则返回-1
        /// </summary>
        public virtual int SoftDelete(T model)
        {
            if (!DbTableAttribute.HasSoftDelete)
            {
                return -1;
            }
            if (CheckColumnInfoFail(model))
            {
                return 0;
            }
            StringBuilder strSql = new StringBuilder();
            List<DBParameter> dbParameters = new List<DBParameter>();
            strSql.Append("UPDATE ");
            strSql.Append(DbTableAttribute.TableName);
            strSql.Append(" SET ");
            strSql.Append(DbTableAttribute.SoftDeleteColumn);
            strSql.Append(" =");
            strSql.Append(DbHelper.DataParamterPrefixChar);
            strSql.Append(DbTableAttribute.SoftDeleteColumn);
            dbParameters.Add(new DBParameter(DbHelper.DataParamterPrefixChar + DbTableAttribute.SoftDeleteColumn, DbTableAttribute.SoftDeleteValue));
            strSql.Append(" WHERE ");
            strSql.Append(BuilderSqlWhere(model, m_PrimaryList, dbParameters, true));
            int count = DbHelper.ExecuteSql(strSql.ToString(), dbParameters.ToArray());
            if (count > 0)
            {
                model.ClearChanges();
            }
            return count;
        }
        #endregion

        #region 私有方法,查找T的数据库字段
        /// <summary>
        /// 创建Update 的Where 条件的sql语句
        /// </summary>
        /// <param name="model">实例对象</param>
        /// <param name="pkList">需要作为条件使用的字段</param>
        /// <param name="dbParameters">SQL参数变量</param>
        /// <param name="containSoftDelete ">是否包含软删除的逻辑</param>
        /// <returns></returns>
        private StringBuilder BuilderSqlWhereForUpdate(T model, List<DbColumnAttribute> pkList, List<DBParameter> dbParameters, bool containSoftDelete = false)
        {
            if (null == dbParameters)
            {
                dbParameters = new List<DBParameter>();
            }
            StringBuilder sqlWhere = new StringBuilder();
            Type typeObj = typeof(T);
            pkList.ForEach(t =>
            {
                if (sqlWhere.Length > 0)
                {
                    sqlWhere.Append(" AND ");
                }
                sqlWhere.Append(t.ColumnName);
                sqlWhere.Append(" =");
                sqlWhere.Append(DbHelper.DataParamterPrefixChar);
                sqlWhere.Append(t.ColumnName + "__pk");

                DBParameter parameter = new DBParameter(DbHelper.DataParamterPrefixChar + t.ColumnName + "__pk");
                parameter.DbType = t.DbType;
                parameter.Size = t.Length;
                object objValue = null;
                if (model.PKPropertyChanges.TryGetValue(t.ColumnName, out objValue))
                {
                    parameter.Value = objValue;
                }
                else
                {
                    parameter.Value = typeObj.GetProperty(t.PropertyName).GetValue(model);
                }
                dbParameters.Add(parameter);
            });
            if (DbTableAttribute.HasSoftDelete)
            {
                if (sqlWhere.Length > 0)
                {
                    sqlWhere.Append(" AND ");
                }
                sqlWhere.Append(DbTableAttribute.SoftDeleteColumn);
                sqlWhere.Append(" =");
                sqlWhere.Append(DbHelper.DataParamterPrefixChar + DbTableAttribute.SoftDeleteColumn + "_");
                DBParameter parameter = new DBParameter(DbHelper.DataParamterPrefixChar + DbTableAttribute.SoftDeleteColumn + "_");
                parameter.Value = DbTableAttribute.EffectiveValue;

                dbParameters.Add(parameter);
            }
            return sqlWhere;
        }


        /// <summary>
        /// 创建Where 条件的sql语句
        /// </summary>
        /// <param name="model">实例对象</param>
        /// <param name="list">需要作为条件使用的字段</param>
        /// <param name="dbParameters">SQL参数变量</param>
        /// <param name="containSoftDelete ">是否包含软删除的逻辑</param>
        /// <returns></returns>
        private StringBuilder BuilderSqlWhere(T model, List<DbColumnAttribute> list, List<DBParameter> dbParameters, bool containSoftDelete = false)
        {
            if (null == dbParameters)
            {
                dbParameters = new List<DBParameter>();
            }
            StringBuilder sqlWhere = new StringBuilder();
            Type typeObj = typeof(T);
            list.ForEach(t =>
            {
                if (sqlWhere.Length > 0)
                {
                    sqlWhere.Append(" AND ");
                }
                sqlWhere.Append(t.ColumnName);
                sqlWhere.Append(" =");
                sqlWhere.Append(DbHelper.DataParamterPrefixChar);
                sqlWhere.Append(t.ColumnName);

                DBParameter parameter = new DBParameter(DbHelper.DataParamterPrefixChar + t.ColumnName);
                parameter.DbType = t.DbType;
                parameter.Size = t.Length;
                parameter.Value = typeObj.GetProperty(t.PropertyName).GetValue(model);
                dbParameters.Add(parameter);
            });
            if (DbTableAttribute.HasSoftDelete)
            {
                if (sqlWhere.Length > 0)
                {
                    sqlWhere.Append(" AND ");
                }
                sqlWhere.Append(DbTableAttribute.SoftDeleteColumn);
                sqlWhere.Append(" =");
                sqlWhere.Append(DbHelper.DataParamterPrefixChar + DbTableAttribute.SoftDeleteColumn + "_");
                DBParameter parameter = new DBParameter(DbHelper.DataParamterPrefixChar + DbTableAttribute.SoftDeleteColumn + "_");
                parameter.Value = DbTableAttribute.EffectiveValue;

                dbParameters.Add(parameter);
            }
            return sqlWhere;
        }

        /// <summary>
        /// 校验是否可以创建SQL Where 语句
        /// </summary>
        /// <param name="model"></param>
        /// <param name="hasPrimary">是否强制要求主键必须填</param>
        /// <returns></returns>
        private bool CheckColumnInfoFail(T model, bool hasPrimary = true)
        {
            return (null == model
                || null == DbTableAttribute
                || null == m_DbColumnAttributes
                || m_DbColumnAttributes.Count == 0
                || (hasPrimary && (null == m_PrimaryList || m_PrimaryList.Count == 0))
                || model.PropertyChanges.Count == 0
                );
        }

        /// <summary>
        /// 根据泛型属性 获取表字段信息
        /// </summary>
        /// <returns></returns>
        private static List<ORM.EntityAttribute.DbColumnAttribute> GetColumnInfo()
        {
            List<ORM.EntityAttribute.DbColumnAttribute> result = new List<DbColumnAttribute>();
            foreach (PropertyInfo info in m_PropertyInfolist)
            {
                var attributes = info.GetCustomAttributes(typeof(DbColumnAttribute), true);
                if (attributes.Length > 0)
                {
                    DbColumnAttribute attr = attributes[0] as DbColumnAttribute;
                    if (null == attr || attr.Ignore)
                    {
                        continue;
                    }
                    attr.PropertyName = info.Name;
                    result.Add(attr);
                }
            }
            return result;
        }
        /// <summary>
        /// 获取主键列信息
        /// </summary>
        /// <returns></returns>
        private static List<ORM.EntityAttribute.DbColumnAttribute> GetPrimaryColumnInfo()
        {
            var list = GetColumnInfo();
            if (null == list || list.Count == 0)
            {
                return null;
            }
            return list.FindAll(t => t.IsPrimary);
        }
        #endregion
        
    }
}
